﻿using System;

namespace Useful.Reflection
{
    public class ComObject : IDisposable
    {
        private object _obj;
        private readonly object _name;

        public object Handle
        {
            get
            {
                return _obj;
            }
        }

        public ComObject(object obj, string name)
        {
            _obj = obj;
            _name = name;
        }

        public void Dispose()
        {
            if (_obj != null && _obj.GetType().IsCOMObject)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(_obj);
                _obj = null;
            }
        }

        public ComObject GetProperty(string name)
        {
            object obj = GetProperty(Handle, name);

            if (obj != null)
                return new ComObject(obj, name);
            else
                return null;
        }

        public T GetProperty<T>(string name)
        {
            object obj = GetProperty(Handle, name);

            if (obj != null)
            {
                return (T)obj;
            }
            else
            {
                if (!typeof(T).IsValueType)
                    return default(T);
                else
                    throw new Exception("Для ValueType возвращено null");
            }
        }

        public ComObject GetProperty(string name, params object[] args)
        {
            object obj = GetPropertyByIndex(Handle, name, args);

            if (obj != null)
                return new ComObject(obj, name);
            else
                return null;
        }

        public T GetProperty<T>(string name, params object[] args)
        {
            object obj = GetPropertyByIndex(Handle, name, args);

            if (obj != null)
            {
                return (T)obj;
            }
            else
            {
                if (!typeof(T).IsValueType)
                    return default(T);
                else
                    throw new Exception("Для ValueType возвращено null");
            }
        }

        public void SetProperty(string name, object value)
        {
            SetProperty(Handle, name, value);
        }

        public void SafeSetProperty(string name, object value)
        {
            try
            {
                SetProperty(name, value);
            }
            catch (Exception ex)
            {
                if (BaseComLateBounding.IsComObjectAccessible(ex)) // Если RPC-сервер доступен, то сгенерировать исключение
                    throw;
            }
        }

        public ComObject InvokeMethod(string name, params object[] args)
        {
            object obj = InvokeMethod(Handle, name, args);

            if (obj != null)
                return new ComObject(obj, name);
            else
                return null;
        }

        public ComObject SafeInvokeMethod(string name, params object[] args)
        {
            ComObject res = null;

            try
            {
                res = InvokeMethod(name, args);

                return res;
            }
            catch (Exception ex)
            {
                if (BaseComLateBounding.IsComObjectAccessible(ex)) // Если RPC-сервер доступен, то сгенерировать исключение
                    throw;
            }

            return res;
        }


        public T InvokeMethod<T>(string name, params object[] args)
        {
            object obj = InvokeMethod(Handle, name, args);

            if (obj != null)
            {
                return (T)obj;
            }
            else
            {
                if (!typeof(T).IsValueType)
                    return default(T);
                else
                    throw new Exception("Для ValueType возвращено null");
            }
        }

        #region Static
        protected static void SetProperty(object obj, string name, object value)
        {
            ReflectionHelper.SetPropertyValue(obj, name, value);
        }

        protected static object GetProperty(object obj, string name)
        {
            return ReflectionHelper.GetProperty(name, obj);
        }

        protected static object GetPropertyByIndex(object obj, string name, params object[] indexes)
        {
            return ReflectionHelper.GetPropertyByIndex(name, obj, indexes);
        }

        protected static object InvokeMethod(object obj, string name, params object[] args)
        {
            if (obj != null)
                return ReflectionHelper.InvokeMethod(obj, name, args);
            else
                throw new NullReferenceException();
        }
        #endregion
    }
}
